package org.beynet.docs.options;

import java.io.File;
import java.io.PrintStream;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import org.beynet.docs.http.BandwithSaver;
import org.beynet.docs.sync.SyncStatus;

/**
 * class handling all the options of the sync process read from the command line.
 * @author beynet
 *
 */
public final class CommandLineOptions {

    public CommandLineOptions(String[] options, PrintStream os) throws IllegalArgumentException {
        this.source = new ArrayList<String>();
        this.options = options;
        this.os = os;
        this.httpchunksize = null;
        this.maxBandwidth = Integer.valueOf(0);
        this.logLevel = LOG_NONE;
        try {
            checkCommandLine();
        } catch (IllegalArgumentException e) {
            os.println(e.getMessage());
            printHelp();
            throw e;
        }
        // creating bandwithsaver singleton
        // --------------------------------
        BandwithSaver.getInstance(getMaxBandwidth());
        if (resume() == false && getStatusFile() != null) {
            File file = new File(getStatusFile());
            if (file.exists()) {
                file.delete();
            }
        }
        if (getStatusFile() != null) {
            SyncStatus.make(getStatusFile());
        }
    }

    /**
     * construct all the option from the arguments
     * @throws InvalidParameterException
     */
    private final void checkCommandLine() throws IllegalArgumentException {
        String previous = null ;
        for (String option : options) {
            // no previous context
            if (previous==null) {
                if (OPTION_DELETE.equals(option)) {
                    delete = true;
                }
                else if (OPTION_LOG.equals(option)) {
                    previous = OPTION_LOG;
                }
                else if (OPTION_MAX_BANDWIDTH.equals(option)) {
                    previous = OPTION_MAX_BANDWIDTH;
                }
                else if (OPTION_RESUME.equals(option)) {
                    resume = true;
                }
                else if (OPTION_GUI.equals(option)) {
                    gui = true;
                }
                else if (OPTION_DESTINATION.equals(option)) {
                    if (destination!=null) throw new IllegalArgumentException("destination already provided");
                    previous = OPTION_DESTINATION;
                }
                else if (OPTION_STATUSFILE.equals(option)) {
                    if (statusFile!=null) throw new IllegalArgumentException("status file already provided");
                    previous = OPTION_STATUSFILE;
                }
                else if (OPTION_SOURCE.equals(option)) {
                    previous = OPTION_SOURCE;
                }
                else if (OPTION_HTTP_CHUNK_SIZE.equals(option)) {
                    if (httpchunksize!=null)  throw new IllegalArgumentException("httpchunck size already provided");
                    previous = OPTION_HTTP_CHUNK_SIZE;
                }
                else if (OPTION_MAX_UPLOAD_THREADS.equals(option)) {
                    if (maxuploadthreads!=null)  throw new IllegalArgumentException("max upload therads already provided");
                    previous = OPTION_MAX_UPLOAD_THREADS;
                }
                else {
                    throw new IllegalArgumentException("Option "+option+" unknown");
                }
            }
            else {
                if (OPTION_DESTINATION.equals(previous)) {
                    destination = option;
                }
                else if (OPTION_MAX_BANDWIDTH.equals(previous)) {
                    try {
                        maxBandwidth = Integer.valueOf(option,10);
                    }catch(NumberFormatException e) {
                        throw new IllegalArgumentException("the max bandwidth option must be an integer (max number of bytes per sec)");
                    }
                }
                else if (OPTION_STATUSFILE.equals(previous)) {
                    statusFile = option;
                }
                else if (OPTION_LOG.equals(previous)) {
                    logLevel = option;
                    if (!LOG_DEBUG.equalsIgnoreCase(logLevel) && !LOG_INFO.equalsIgnoreCase(logLevel)) {
                        throw new IllegalArgumentException("invalid log level:"+logLevel);
                    }
                }
                else if (OPTION_SOURCE.equals(previous)) {
                    source.add(option);
                }
                else if (OPTION_HTTP_CHUNK_SIZE.equals(previous)) {
                    int local=Integer.parseInt(option);
                    httpchunksize = Integer.valueOf(local * MIN_HTTP_CHUNK_SIZE.intValue()) ;
                }
                else if (OPTION_MAX_UPLOAD_THREADS.equals(previous)) {
                    maxuploadthreads=Integer.valueOf(option,10);
                }
                previous=null;
            }
        }
        if (gui != true) {
            if (resume == false && (destination == null || source == null)) {
                throw new IllegalArgumentException("missing required argument");
            }
            if (resume==true && statusFile==null) throw new IllegalArgumentException("missing required argument");
        }
    }
    
    /**
     * @return a size in bytes : each file greater than this size will be uploaded
     * using google docs resumable uploads technology.
     */
    public Integer getHttpChunkSize() {
        if (httpchunksize==null) return(DEFAULT_HTTP_CHUNK_SIZE);
        return(httpchunksize);
    }
    
    public boolean guid(){
        return(gui);
    }

    /**
     * @return the maximum number of byte allowed to be sent per seconds
     */
    public Integer getMaxBandwidth() {
        return(maxBandwidth);
    }
    
    /**
     * return the maximum number of thread to create for upload
     * @return
     */
    public Integer getMaxUploadThreads() {
        if (maxuploadthreads==null) return(DEFAULT_MAX_UPLOAD_THREADS);
        return(maxuploadthreads);
    }
    
    /**
     * return true if deleteRemovedOption was provided
     * @return
     */
    public boolean deleteRemoved() {
        return(delete);
    }
    
    public String getLogLevel() {
        return(logLevel);
    }
    
    /**
     * return the destination folder where we will copy the source
     * @return
     */
    public String getDestinationFolder() {
        return(destination);
    }
    
    /**
     * return the source file or folder to copy
     * @return
     */
    public List<String> getSource() {
        return(source);
    }
    
    public String getStatusFile() {
        return(statusFile);
    }
    
    /**
     * @return true if user request a resume
     */
    public boolean resume() {
        return(resume);
    }
    /**
     * print help
     * paralele 
     */
    public final void printHelp() {
        StringBuilder options = new StringBuilder("Mandatory parameters : \n");
                                   options.append("-----------------------\n");
        options.append(OPTION_SOURCE);
        options.append(" <folder or file to copy> : provide the file or folder to save. This parameter is repeatable.\n");
        options.append(OPTION_DESTINATION);
        options.append(" <destination folder> : provide the destination folder into which we will copy.\n");
        options.append("\nOptional parameters :\n");
        options.append("---------------------\n");
        options.append(OPTION_LOG);
        options.append(" <");
        options.append(LOG_INFO);
        options.append("|");
        options.append(LOG_DEBUG);
        options.append("|");
        options.append(LOG_NONE);
        options.append("> : enable logging with provided level.\n");
        options.append(OPTION_DELETE);
        options.append(" : if non existing file on the source side are to be removed on the destination side.\n");
        options.append(OPTION_STATUSFILE);
        options.append(" : file where urls generated for upload are stored - to make upload resumable.\n");
        options.append(OPTION_RESUME);
        options.append(" : try to resume a previous upload.\n");
        options.append(OPTION_HTTP_CHUNK_SIZE);
        options.append(" : files bigger than 512*1024*N bytes (where N is the user provided parameter) will be send \n");
        options.append("\tin a separate thread using gdocs resumable upload.\n");
        options.append("\tEvery post size will be equal to 512*1024*N bytes. Default http chunk size is ");
        options.append(DEFAULT_HTTP_CHUNK_SIZE);
        options.append(".\n");
        options.append(OPTION_MAX_UPLOAD_THREADS);
        options.append(" : The maximum number of thread created for resumable uploads - ie the maximum number of parallel uploads\n");
        options.append("\tdefault max number of thread is ");
        options.append(DEFAULT_MAX_UPLOAD_THREADS);
        options.append(".\n");
        options.append(OPTION_MAX_BANDWIDTH);
        options.append(" : The maximum bandwidth to use (the maximum number of byte allowed to be sent per second) : to avoid consuming to much network\n");
        options.append("\tdefault is 0 - ie no maximum");
        os.println(options.toString());
    }


    private String[]     options ;
    private String       statusFile;
    private PrintStream  os ;
    private boolean      delete =false;
    private boolean      resume =false;
    private String       logLevel ;
    private List<String> source ;
    private String       destination ;
    private Integer      httpchunksize ;
    private Integer      maxuploadthreads ;
    private Integer      maxBandwidth ;
    private boolean      gui=false         ;
    
    
    public final static  String  OPTION_MAX_BANDWIDTH       = "--maxbandwidth" ;
    public final static  String  OPTION_DELETE             = "--delete" ;
    public final static  String  OPTION_LOG                = "--log" ;
    public final static  String  OPTION_DESTINATION        = "--destination" ;
    public final static  String  OPTION_SOURCE             = "--source" ;
    public final static  String  OPTION_RESUME             = "--resume" ;
    public final static  String  OPTION_STATUSFILE         = "--statusfile" ;
    public final static  String  OPTION_HTTP_CHUNK_SIZE    = "--httpchunksize" ;
    public final static  String  OPTION_MAX_UPLOAD_THREADS = "--maxuploadthreads";
    public final static  String  OPTION_GUI                = "--gui";
    public final static  String  LOG_NONE                  ="ERROR";
    public final static  String  LOG_INFO                  ="INFO";
    public final static  String  LOG_DEBUG                  ="DEBUG";
    private final static Integer DEFAULT_MAX_UPLOAD_THREADS = Integer.valueOf(1) ;  
    private static final Integer MIN_HTTP_CHUNK_SIZE = Integer.valueOf(512*1024);
    private static final Integer DEFAULT_HTTP_CHUNK_SIZE = Integer.valueOf(MIN_HTTP_CHUNK_SIZE.intValue()*2);
}
